/*
  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/

#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>

/* project headers */
#include "cJSON.h"

/* define constants */
#define true 1
#define false 0
#define null ((void*)0)

/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6

#define cJSON_IsReference 256
#define cJSON_StringIsConst 512

/* The cJSON structure: */
typedef struct cJSON {
    struct cJSON *next, *prev;
    struct cJSON *child;
    int type;
    char *valuestring;
    int valueint;
    double valuedouble;
    char *string;
} cJSON;

static const char *ep;

static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };

static unsigned parse_hex4(const char *str) {
    unsigned h = 0;
    if (*str >= '0' && *str <= '9') h += (*str) - '0'; else if (*str >= 'A' && *str <= 'F') h += 10 + (*str) - 'A'; else if (*str >= 'a' && *str <= 'f') h += 10 + (*str) - 'a'; else return 0;
    h <<= 4;
    str++;
    if (*str >= '0' && *str <= '9') h += (*str) - '0'; else if (*str >= 'A' && *str <= 'F') h += 10 + (*str) - 'A'; else if (*str >= 'a' && *str <= 'f') h += 10 + (*str) - 'a'; else return 0;
    h <<= 4;
    str++;
    if (*str >= '0' && *str <= '9') h += (*str) - '0'; else if (*str >= 'A' && *str <= 'F') h += 10 + (*str) - 'A'; else if (*str >= 'a' && *str <= 'f') h += 10 + (*str) - 'a'; else return 0;
    h <<= 4;
    str++;
    if (*str >= '0' && *str <= '9') h += (*str) - '0'; else if (*str >= 'A' && *str <= 'F') h += 10 + (*str) - 'A'; else if (*str >= 'a' && *str <= 'f') h += 10 + (*str) - 'a'; else return 0;
    return h;
}

static unsigned char *print_string_ptr(const unsigned char *str) {
    const unsigned char *ptr;
    unsigned char *ptr2;
    unsigned char *out;
    int len = 0;
    unsigned uc, uc2;

    if (!str) return cJSON_strdup("");

    ptr = str;
    while ((uc = *ptr++) != 0) {
        if (uc < 0x20 || uc >= 0x7f || uc == '\\' || uc == '"') {
            len += 2;
            if (uc >= 0xd800 && uc <= 0xdbff && ptr[0] >= 0xdc00 && ptr[0] <= 0xdfff) {
                len += 2;
                ptr++;
            }
        } else len++;
    }

    out = (unsigned char*)cJSON_malloc(len + 3);
    if (!out) return 0;

    ptr2 = out;
    ptr = str;
    *ptr2++ = '"';
    while ((uc = *ptr++) != 0) {
        if ((unsigned char) uc > 0x7f || uc < 0x20 || uc == '\\' || uc == '"') {
            *ptr2++ = '\\';
            switch (uc) {
                case '"': *ptr2++ = '"'; break;
                case '\\': *ptr2++ = '\\'; break;
                case '\b': *ptr2++ = 'b'; break;
                case '\f': *ptr2++ = 'f'; break;
                case '\n': *ptr2++ = 'n'; break;
                case '\r': *ptr2++ = 'r'; break;
                case '\t': *ptr2++ = 't'; break;
                default:
                    *ptr2++ = 'u';
                    *ptr2++ = "0123456789ABCDEF"[uc >> 12];
                    *ptr2++ = "0123456789ABCDEF"[(uc >> 8) & 15];
                    *ptr2++ = "0123456789ABCDEF"[(uc >> 4) & 15];
                    *ptr2++ = "0123456789ABCDEF"[uc & 15];
                    break;
            }
        } else *ptr2++ = uc;
    }
    *ptr2++ = '"';
    *ptr2++ = 0;
    return out;
}

static unsigned char *print_string(const unsigned char *str) {
    unsigned char *out;
    out = print_string_ptr(str);
    if (out) {
        out = (unsigned char*)cJSON_strdup((const char*)out);
        cJSON_free(out);
    }
    return out;
}

static const char *parse_string(const char *str, char **item) {
    const char *ptr = str + 1;
    char *ptr2;
    char *out;
    int len = 0;
    unsigned uc, uc2;

    if (*str != '"') return 0;

    while (*ptr != '"' && *ptr != 0) {
        if (*ptr++ == '\\') {
            if (*ptr == 0) break;
            ptr++;
        }
        len++;
    }

    out = (char*)cJSON_malloc(len + 1);
    if (!out) return 0;

    ptr = str + 1;
    ptr2 = out;
    while (*ptr != '"' && *ptr != 0) {
        if (*ptr == '\\') {
            ptr++;
            if (*ptr == 0) break;
            switch (*ptr) {
                case '"': *ptr2 = '"'; break;
                case '\\': *ptr2 = '\\'; break;
                case '/': *ptr2 = '/'; break;
                case 'b': *ptr2 = '\b'; break;
                case 'f': *ptr2 = '\f'; break;
                case 'n': *ptr2 = '\n'; break;
                case 'r': *ptr2 = '\r'; break;
                case 't': *ptr2 = '\t'; break;
                case 'u':
                    ptr++;
                    uc = parse_hex4(ptr);
                    ptr += 4;

                    if ((uc >= 0xd800) && (uc <= 0xdbff)) {
                        if (*ptr == '\\' && *(ptr + 1) == 'u') {
                            ptr += 2;
                            uc2 = parse_hex4(ptr);
                            ptr += 4;
                            if ((uc2 >= 0xdc00) && (uc2 <= 0xdfff)) {
                                uc = 0x10000 + ((uc & 0x3ff) << 10) + (uc2 & 0x3ff);
                            }
                        }
                    }

                    len = 4;
                    if (uc < 0x80) len = 1;
                    else if (uc < 0x800) len = 2;
                    else if (uc < 0x10000) len = 3;
                    if (len > 4) len = 4; else ptr2[len] = 0;

                    switch (len) {
                        case 4:
                            ptr2[3] = ((uc | 0x80) & 0xbf);
                            uc >>= 6;
                        case 3:
                            ptr2[2] = ((uc | 0x80) & 0xbf);
                            uc >>= 6;
                        case 2:
                            ptr2[1] = ((uc | 0x80) & 0xbf);
                            uc >>= 6;
                        case 1:
                            ptr2[0] = (uc | firstByteMark[len]);
                    }
                    ptr2 += len;
                    continue;
                default: *ptr2 = *ptr; break;
            }
        } else *ptr2 = *ptr;
        ptr++;
        ptr2++;
    }
    *ptr2 = 0;
    if (*ptr == '"') ptr++;
    *item = out;
    return ptr;
}

static const char *parse_number(const char *num, double *n) {
    char *end;
    if (!num || !n || !*num) return 0;
    *n = strtod(num, &end);
    if (end == num) return 0;
    return end;
}

static char *cJSON_strdup(const char *str) {
    size_t len;
    char *copy;

    if (!str) return 0;
    len = strlen(str) + 1;
    copy = (char*)cJSON_malloc(len);
    if (copy) memcpy(copy, str, len);
    return copy;
}

void *cJSON_malloc(size_t sz) {\ return malloc(sz); }
void cJSON_free(void *ptr) { if (ptr) free(ptr); }

static cJSON *cJSON_New_Item(void) {
    cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
    if (node) memset(node, 0, sizeof(cJSON));
    return node;
}

cJSON *cJSON_Parse(const char *value) {
    cJSON *c = 0;
    ep = 0;
    if (value) c = parse_value(value);
    return c;
}

static cJSON *parse_value(const char *value);
static cJSON *parse_array(const char *value);
static cJSON *parse_object(const char *value);

static cJSON *parse_value(const char *value) {
    if (!value) return 0;
    if (!strncmp(value, "null", 4)) return cJSON_CreateNull();
    if (!strncmp(value, "false", 5)) return cJSON_CreateFalse();
    if (!strncmp(value, "true", 4)) return cJSON_CreateTrue();
    if (*value == '"') return cJSON_CreateString("");
    if (*value == '{') return parse_object(value);
    if (*value == '[') return parse_array(value);
    if ((*value == '-') || ((*value >= '0') && (*value <= '9'))) return cJSON_CreateNumber(0);
    ep = value;
    return 0;
}

static cJSON *parse_array(const char *value) {
    cJSON *n = 0, *p = 0, *c = 0;

    if (*value != '[') return 0;
    value = skip(value + 1);
    if (*value == ']') return cJSON_CreateArray();
    n = cJSON_New_Item();
    if (!n) return 0;
    n->type = cJSON_Array;
    value = skip(parse_value(value));
    if (!value) goto fail;
    c = n->child = (cJSON*)value;
    if (!c) goto fail;
    while (*value == ',') {
        p = c;
        value = skip(value + 1);
        if (*value == ']') {
            p->next = 0;
            break;
        }
        value = skip(parse_value(value));
        if (!value) goto fail;
        c = (cJSON*)value;
        if (!c) goto fail;
        p->next = c;
        c->prev = p;
    }
    if (*value != ']') goto fail;
    return n;
 fail:
    cJSON_Delete(n);
    return 0;
}

static cJSON *parse_object(const char *value) {
    cJSON *n = 0, *p = 0, *c = 0;
    char *str = 0;

    if (*value != '{') return 0;
    value = skip(value + 1);
    if (*value == '}') return cJSON_CreateObject();
    n = cJSON_New_Item();
    if (!n) return 0;
    n->type = cJSON_Object;
    while (1) {
        value = skip(value);
        if (*value != '"') goto fail;
        value = parse_string(value, &str);
        if (!value) goto fail;
        value = skip(value);
        if (*value != ':') goto fail;
        p = c;
        c = cJSON_New_Item();
        if (!c) goto fail;
        if (p) {
            p->next = c;
            c->prev = p;
        } else n->child = c;
        c->string = str;
        value = skip(value + 1);
        value = skip(parse_value(value));
        if (!value) goto fail;
        if (*value == '}') break;
        if (*value != ',') goto fail;
        value = value + 1;
    }
    return n;
 fail:
    cJSON_free(str);
    cJSON_Delete(n);
    return 0;
}

static const char *skip(const char *in) {
    while (in && *in && (unsigned char)*in <= 32) in++;
    return in;
}

void cJSON_Delete(cJSON *c) {
    cJSON *next;
    while (c) {
        next = c->next;
        if (!(c->type & cJSON_IsReference) && c->child) cJSON_Delete(c->child);
        if (!(c->type & cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
        if (c->string && !(c->type & cJSON_StringIsConst)) cJSON_free(c->string);
        cJSON_free(c);
        c = next;
    }
}

cJSON *cJSON_CreateNull(void) {
    cJSON *item = cJSON_New_Item();
    if (item) item->type = cJSON_NULL;
    return item;
}

cJSON *cJSON_CreateTrue(void) {
    cJSON *item = cJSON_New_Item();
    if (item) item->type = cJSON_True;
    return item;
}

cJSON *cJSON_CreateFalse(void) {
    cJSON *item = cJSON_New_Item();
    if (item) item->type = cJSON_False;
    return item;
}

cJSON *cJSON_CreateBool(int b) {
    cJSON *item = cJSON_New_Item();
    if (item) item->type = b ? cJSON_True : cJSON_False;
    return item;
}

cJSON *cJSON_CreateNumber(double num) {
    cJSON *item = cJSON_New_Item();
    if (item) {
        item->type = cJSON_Number;
        item->valuedouble = num;
        item->valueint = (int)num;
    }
    return item;
}

cJSON *cJSON_CreateString(const char *string) {
    cJSON *item = cJSON_New_Item();
    if (item) {
        item->type = cJSON_String;
        item->valuestring = cJSON_strdup(string);
        if (!item->valuestring) {
            cJSON_Delete(item);
            return 0;
        }
    }
    return item;
}

cJSON *cJSON_CreateArray(void) {
    cJSON *item = cJSON_New_Item();
    if (item) item->type = cJSON_Array;
    return item;
}

cJSON *cJSON_CreateObject(void) {
    cJSON *item = cJSON_New_Item();
    if (item) item->type = cJSON_Object;
    return item;
}

void cJSON_AddItemToArray(cJSON *array, cJSON *item) {
    cJSON *c = array->child;
    if (!item) return;
    if (!array || !array->type == cJSON_Array) {
        cJSON_Delete(item);
        return;
    }
    item->prev = 0;
    if (!c) {
        array->child = item;
        item->next = 0;
    } else {
        while (c && c->next) c = c->next;
        c->next = item;
        item->prev = c;
        item->next = 0;
    }
}

void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) {
    if (!item) return;
    if (!object || !object->type == cJSON_Object || !string) {
        cJSON_Delete(item);
        return;
    }
    item->string = cJSON_strdup(string);
    if (!item->string) {
        cJSON_Delete(item);
        return;
    }
    cJSON_AddItemToArray(object, item);
}

void cJSON_AddNullToObject(cJSON *object, const char *name) {
    cJSON_AddItemToObject(object, name, cJSON_CreateNull());
}

void cJSON_AddTrueToObject(cJSON *object, const char *name) {
    cJSON_AddItemToObject(object, name, cJSON_CreateTrue());
}

void cJSON_AddFalseToObject(cJSON *object, const char *name) {
    cJSON_AddItemToObject(object, name, cJSON_CreateFalse());
}

void cJSON_AddBoolToObject(cJSON *object, const char *name, int b) {
    cJSON_AddItemToObject(object, name, cJSON_CreateBool(b));
}

void cJSON_AddNumberToObject(cJSON *object, const char *name, double n) {
    cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n));
}

void cJSON_AddStringToObject(cJSON *object, const char *name, const char *string) {
    cJSON_AddItemToObject(object, name, cJSON_CreateString(string));
}

cJSON *cJSON_GetObjectItem(cJSON *object, const char *string) {
    cJSON *c = object ? object->child : 0;
    while (c && c->string) {
        if (!strcmp(c->string, string)) return c;
        c = c->next;
    }
    return 0;
}

int cJSON_GetArraySize(cJSON *array) {
    int i = 0;
    cJSON *c = array ? array->child : 0;
    while (c) {
        i++;
        c = c->next;
    }
    return i;
}

cJSON *cJSON_GetArrayItem(cJSON *array, int index) {
    cJSON *c = array ? array->child : 0;
    while (c && index > 0) {
        index--;
        c = c->next;
    }
    return c;
}

char *cJSON_Print(cJSON *item) {
    return cJSON_PrintUnformatted(item);
}

char *cJSON_PrintUnformatted(cJSON *item) {
    unsigned char *out = 0;
    print_value(item, 0, &out);
    return (char*)out;
}

static int print_value(cJSON *item, int depth, unsigned char **put) {
    int ret = 0;
    if (!item) return 0;
    switch ((item->type) & 255) {
        case cJSON_NULL: ret = sprintf((char*)*put, "null"); *put += ret; break;
        case cJSON_False: ret = sprintf((char*)*put, "false"); *put += ret; break;
        case cJSON_True: ret = sprintf((char*)*put, "true"); *put += ret; break;
        case cJSON_Number: ret = sprintf((char*)*put, "%g", item->valuedouble); *put += ret; break;
        case cJSON_String: ret = print_string((unsigned char*)item->valuestring); *put += ret; break;
        case cJSON_Array: ret = print_array(item, depth, put); break;
        case cJSON_Object: ret = print_object(item, depth, put); break;
    }
    return ret;
}

static int print_array(cJSON *item, int depth, unsigned char **put) {
    int ret = 0, len = 5;
    cJSON *child = item->child;

    while (child) {
        len += 2; child = child->next;
    }
    *put = (unsigned char*)cJSON_malloc(len);
    if (!*put) return -1;

    **put = '[';
    (*put)++;

    child = item->child;
    while (child) {
        print_value(child, depth + 1, put);
        if (child->next) {
            **put = ',';
            (*put)++;
        }
        child = child->next;
    }

    **put = ']';
    (*put)++;
    **put = 0;

    return len;
}

static int print_object(cJSON *item, int depth, unsigned char **put) {
    int ret = 0, len = 5;
    cJSON *child = item->child;

    while (child) {
        len += strlen(child->string) + 3; child = child->next;
    }
    *put = (unsigned char*)cJSON_malloc(len);
    if (!*put) return -1;

    **put = '{';
    (*put)++;

    child = item->child;
    while (child) {
        **put = '"';
        (*put)++;
        memcpy(*put, child->string, strlen(child->string));
        *put += strlen(child->string);
        **put = '"';
        (*put)++;
        **put = ':';
        (*put)++;
        print_value(child, depth + 1, put);
        if (child->next) {
            **put = ',';
            (*put)++;
        }
        child = child->next;
    }

    **put = '}';
    (*put)++;
    **put = 0;

    return len;
}

// 简化版的cJSON接口，仅包含项目所需的功能